home *** CD-ROM | disk | FTP | other *** search
/ X User Tools / X User Tools (O'Reilly and Associates)(1994).ISO / sun4c / archive / tcltk.z / tcltk / slib / tk / demos / tkSquare.c < prev    next >
C/C++ Source or Header  |  1994-09-20  |  17KB  |  544 lines

  1. /* 
  2.  * tkSquare.c --
  3.  *
  4.  *    This module implements "square" widgets.  A "square" is
  5.  *    a widget that displays a single square that can be moved
  6.  *    around and resized.  This file is intended as an example
  7.  *    of how to build a widget.
  8.  *
  9.  * Copyright (c) 1991-1993 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Permission is hereby granted, without written agreement and without
  13.  * license or royalty fees, to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose, provided that the
  15.  * above copyright notice and the following two paragraphs appear in
  16.  * all copies of this software.
  17.  * 
  18.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  19.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  20.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  21.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.  *
  23.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  24.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  25.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  26.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  27.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  28.  */
  29.  
  30. #ifndef lint
  31. static char rcsid[] = "$Header: /user6/ouster/wish/library/demos/RCS/tkSquare.c,v 1.8 93/10/18 15:08:37 ouster Exp $ SPRITE (Berkeley)";
  32. #endif
  33.  
  34. #include "tkConfig.h"
  35. #include "tk.h"
  36.  
  37. /*
  38.  * A data structure of the following type is kept for each square
  39.  * widget managed by this file:
  40.  */
  41.  
  42. typedef struct {
  43.     Tk_Window tkwin;        /* Window that embodies the square.  NULL
  44.                  * means window has been deleted but
  45.                  * widget record hasn't been cleaned up yet. */
  46.     Display *display;        /* X's token for the window's display. */
  47.     Tcl_Interp *interp;        /* Interpreter associated with widget. */
  48.     int x, y;            /* Position of square's upper-left corner
  49.                  * within widget. */
  50.     int size;            /* Width and height of square. */
  51.  
  52.     /*
  53.      * Information used when displaying widget:
  54.      */
  55.  
  56.     int borderWidth;        /* Width of 3-D border around whole widget. */
  57.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  58.     Tk_3DBorder fgBorder;    /* For drawing square. */
  59.     int relief;            /* Indicates whether window as a whole is
  60.                  * raised, sunken, or flat. */
  61.     GC gc;            /* Graphics context for copying from
  62.                  * off-screen pixmap onto screen. */
  63.     int doubleBuffer;        /* Non-zero means double-buffer redisplay
  64.                  * with pixmap;  zero means draw straight
  65.                  * onto the display. */
  66.     int updatePending;        /* Non-zero means a call to SquareDisplay
  67.                  * has already been scheduled. */
  68. } Square;
  69.  
  70. /*
  71.  * Information used for argv parsing.
  72.  */
  73.  
  74. static Tk_ConfigSpec configSpecs[] = {
  75.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  76.     "#cdb79e", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY},
  77.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  78.     "white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY},
  79.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  80.     (char *) NULL, 0, 0},
  81.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  82.     (char *) NULL, 0, 0},
  83.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  84.     "2", Tk_Offset(Square, borderWidth), 0},
  85.     {TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
  86.     "1", Tk_Offset(Square, doubleBuffer), 0},
  87.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  88.     (char *) NULL, 0, 0},
  89.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  90.     "#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY},
  91.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  92.     "black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY},
  93.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  94.     "raised", Tk_Offset(Square, relief), 0},
  95.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  96.     (char *) NULL, 0, 0}
  97. };
  98.  
  99. /*
  100.  * Forward declarations for procedures defined later in this file:
  101.  */
  102.  
  103. static int        SquareConfigure _ANSI_ARGS_((Tcl_Interp *interp,
  104.                 Square *squarePtr, int argc, char **argv,
  105.                 int flags));
  106. static void        SquareDestroy _ANSI_ARGS_((ClientData clientData));
  107. static void        SquareDisplay _ANSI_ARGS_((ClientData clientData));
  108. static void        KeepInWindow _ANSI_ARGS_((Square *squarePtr));
  109. static void        SquareEventProc _ANSI_ARGS_((ClientData clientData,
  110.                 XEvent *eventPtr));
  111. static int        SquareWidgetCmd _ANSI_ARGS_((ClientData clientData,
  112.                 Tcl_Interp *, int argc, char **argv));
  113.  
  114. /*
  115.  *--------------------------------------------------------------
  116.  *
  117.  * SquareCmd --
  118.  *
  119.  *    This procedure is invoked to process the "square" Tcl
  120.  *    command.  It creates a new "square" widget.
  121.  *
  122.  * Results:
  123.  *    A standard Tcl result.
  124.  *
  125.  * Side effects:
  126.  *    A new widget is created and configured.
  127.  *
  128.  *--------------------------------------------------------------
  129.  */
  130.  
  131. int
  132. SquareCmd(clientData, interp, argc, argv)
  133.     ClientData clientData;    /* Main window associated with
  134.                  * interpreter. */
  135.     Tcl_Interp *interp;        /* Current interpreter. */
  136.     int argc;            /* Number of arguments. */
  137.     char **argv;        /* Argument strings. */
  138. {
  139.     Tk_Window main = (Tk_Window) clientData;
  140.     Square *squarePtr;
  141.     Tk_Window tkwin;
  142.  
  143.     if (argc < 2) {
  144.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  145.         argv[0], " pathName ?options?\"", (char *) NULL);
  146.     return TCL_ERROR;
  147.     }
  148.  
  149.     tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
  150.     if (tkwin == NULL) {
  151.     return TCL_ERROR;
  152.     }
  153.     Tk_SetClass(tkwin, "Square");
  154.  
  155.     /*
  156.      * Allocate and initialize the widget record.
  157.      */
  158.  
  159.     squarePtr = (Square *) ckalloc(sizeof(Square));
  160.     squarePtr->tkwin = tkwin;
  161.     squarePtr->display = Tk_Display(tkwin);
  162.     squarePtr->interp = interp;
  163.     squarePtr->x = 0;
  164.     squarePtr->y = 0;
  165.     squarePtr->size = 20;
  166.     squarePtr->borderWidth = 0;
  167.     squarePtr->bgBorder = NULL;
  168.     squarePtr->fgBorder = NULL;
  169.     squarePtr->relief = TK_RELIEF_FLAT;
  170.     squarePtr->gc = None;
  171.     squarePtr->doubleBuffer = 1;
  172.     squarePtr->updatePending = 0;
  173.  
  174.     Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
  175.         SquareEventProc, (ClientData) squarePtr);
  176.     Tcl_CreateCommand(interp, Tk_PathName(squarePtr->tkwin), SquareWidgetCmd,
  177.         (ClientData) squarePtr, (void (*)()) NULL);
  178.     if (SquareConfigure(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) {
  179.     Tk_DestroyWindow(squarePtr->tkwin);
  180.     return TCL_ERROR;
  181.     }
  182.  
  183.     interp->result = Tk_PathName(squarePtr->tkwin);
  184.     return TCL_OK;
  185. }
  186.  
  187. /*
  188.  *--------------------------------------------------------------
  189.  *
  190.  * SquareWidgetCmd --
  191.  *
  192.  *    This procedure is invoked to process the Tcl command
  193.  *    that corresponds to a widget managed by this module.
  194.  *    See the user documentation for details on what it does.
  195.  *
  196.  * Results:
  197.  *    A standard Tcl result.
  198.  *
  199.  * Side effects:
  200.  *    See the user documentation.
  201.  *
  202.  *--------------------------------------------------------------
  203.  */
  204.  
  205. static int
  206. SquareWidgetCmd(clientData, interp, argc, argv)
  207.     ClientData clientData;        /* Information about square widget. */
  208.     Tcl_Interp *interp;            /* Current interpreter. */
  209.     int argc;                /* Number of arguments. */
  210.     char **argv;            /* Argument strings. */
  211. {
  212.     Square *squarePtr = (Square *) clientData;
  213.     int result = TCL_OK;
  214.     int length;
  215.     char c;
  216.  
  217.     if (argc < 2) {
  218.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  219.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  220.     return TCL_ERROR;
  221.     }
  222.     Tk_Preserve((ClientData) squarePtr);
  223.     c = argv[1][0];
  224.     length = strlen(argv[1]);
  225.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  226.     if (argc == 2) {
  227.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  228.             (char *) squarePtr, (char *) NULL, 0);
  229.     } else if (argc == 3) {
  230.         result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
  231.             (char *) squarePtr, argv[2], 0);
  232.     } else {
  233.         result = SquareConfigure(interp, squarePtr, argc-2, argv+2,
  234.             TK_CONFIG_ARGV_ONLY);
  235.     }
  236.     } else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) {
  237.     if ((argc != 2) && (argc != 4)) {
  238.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  239.             argv[0], " position ?x y?\"", (char *) NULL);
  240.         goto error;
  241.     }
  242.     if (argc == 4) {
  243.         if ((Tk_GetPixels(interp, squarePtr->tkwin, argv[2],
  244.             &squarePtr->x) != TCL_OK) || (Tk_GetPixels(interp,
  245.             squarePtr->tkwin, argv[3], &squarePtr->y) != TCL_OK)) {
  246.         goto error;
  247.         }
  248.         KeepInWindow(squarePtr);
  249.     }
  250.     sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y);
  251.     } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
  252.     if ((argc != 2) && (argc != 3)) {
  253.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  254.             argv[0], " size ?amount?\"", (char *) NULL);
  255.         goto error;
  256.     }
  257.     if (argc == 3) {
  258.         int i;
  259.  
  260.         if (Tk_GetPixels(interp, squarePtr->tkwin, argv[2], &i) != TCL_OK) {
  261.         goto error;
  262.         }
  263.         if ((i <= 0) || (i > 100)) {
  264.         Tcl_AppendResult(interp, "bad size \"", argv[2],
  265.             "\"", (char *) NULL);
  266.         goto error;
  267.         }
  268.         squarePtr->size = i;
  269.         KeepInWindow(squarePtr);
  270.     }
  271.     sprintf(interp->result, "%d", squarePtr->size);
  272.     } else {
  273.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  274.         "\":  must be configure, position, or size", (char *) NULL);
  275.     goto error;
  276.     }
  277.     if (!squarePtr->updatePending) {
  278.     Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  279.     squarePtr->updatePending = 1;
  280.     }
  281.     Tk_Release((ClientData) squarePtr);
  282.     return result;
  283.  
  284.     error:
  285.     Tk_Release((ClientData) squarePtr);
  286.     return TCL_ERROR;
  287. }
  288.  
  289. /*
  290.  *----------------------------------------------------------------------
  291.  *
  292.  * SquareConfigure --
  293.  *
  294.  *    This procedure is called to process an argv/argc list in
  295.  *    conjunction with the Tk option database to configure (or
  296.  *    reconfigure) a square widget.
  297.  *
  298.  * Results:
  299.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  300.  *    returned, then interp->result contains an error message.
  301.  *
  302.  * Side effects:
  303.  *    Configuration information, such as colors, border width,
  304.  *    etc. get set for squarePtr;  old resources get freed,
  305.  *    if there were any.
  306.  *
  307.  *----------------------------------------------------------------------
  308.  */
  309.  
  310. static int
  311. SquareConfigure(interp, squarePtr, argc, argv, flags)
  312.     Tcl_Interp *interp;            /* Used for error reporting. */
  313.     Square *squarePtr;            /* Information about widget. */
  314.     int argc;                /* Number of valid entries in argv. */
  315.     char **argv;            /* Arguments. */
  316.     int flags;                /* Flags to pass to
  317.                      * Tk_ConfigureWidget. */
  318. {
  319.     if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs,
  320.         argc, argv, (char *) squarePtr, flags) != TCL_OK) {
  321.     return TCL_ERROR;
  322.     }
  323.  
  324.     /*
  325.      * Set the background for the window and create a graphics context
  326.      * for use during redisplay.
  327.      */
  328.  
  329.     Tk_SetWindowBackground(squarePtr->tkwin,
  330.         Tk_3DBorderColor(squarePtr->bgBorder)->pixel);
  331.     if ((squarePtr->gc == None) && (squarePtr->doubleBuffer)) {
  332.     XGCValues gcValues;
  333.     gcValues.function = GXcopy;
  334.     gcValues.graphics_exposures = False;
  335.     squarePtr->gc = Tk_GetGC(squarePtr->tkwin,
  336.         GCFunction|GCGraphicsExposures, &gcValues);
  337.     }
  338.  
  339.     /*
  340.      * Register the desired geometry for the window.  Then arrange for
  341.      * the window to be redisplayed.
  342.      */
  343.  
  344.     Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
  345.     Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth);
  346.     if (!squarePtr->updatePending) {
  347.     Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  348.     squarePtr->updatePending = 1;
  349.     }
  350.     return TCL_OK;
  351. }
  352.  
  353. /*
  354.  *--------------------------------------------------------------
  355.  *
  356.  * SquareEventProc --
  357.  *
  358.  *    This procedure is invoked by the Tk dispatcher for various
  359.  *    events on squares.
  360.  *
  361.  * Results:
  362.  *    None.
  363.  *
  364.  * Side effects:
  365.  *    When the window gets deleted, internal structures get
  366.  *    cleaned up.  When it gets exposed, it is redisplayed.
  367.  *
  368.  *--------------------------------------------------------------
  369.  */
  370.  
  371. static void
  372. SquareEventProc(clientData, eventPtr)
  373.     ClientData clientData;    /* Information about window. */
  374.     XEvent *eventPtr;        /* Information about event. */
  375. {
  376.     Square *squarePtr = (Square *) clientData;
  377.  
  378.     if (eventPtr->type == Expose) {
  379.     if (!squarePtr->updatePending) {
  380.         Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  381.         squarePtr->updatePending = 1;
  382.     }
  383.     } else if (eventPtr->type == ConfigureNotify) {
  384.     KeepInWindow(squarePtr);
  385.     if (!squarePtr->updatePending) {
  386.         Tk_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
  387.         squarePtr->updatePending = 1;
  388.     }
  389.     } else if (eventPtr->type == DestroyNotify) {
  390.     Tcl_DeleteCommand(squarePtr->interp, Tk_PathName(squarePtr->tkwin));
  391.     squarePtr->tkwin = NULL;
  392.     if (squarePtr->updatePending) {
  393.         Tk_CancelIdleCall(SquareDisplay, (ClientData) squarePtr);
  394.     }
  395.     Tk_EventuallyFree((ClientData) squarePtr, SquareDestroy);
  396.     }
  397. }
  398.  
  399. /*
  400.  *--------------------------------------------------------------
  401.  *
  402.  * SquareDisplay --
  403.  *
  404.  *    This procedure redraws the contents of a square window.
  405.  *    It is invoked as a do-when-idle handler, so it only runs
  406.  *    when there's nothing else for the application to do.
  407.  *
  408.  * Results:
  409.  *    None.
  410.  *
  411.  * Side effects:
  412.  *    Information appears on the screen.
  413.  *
  414.  *--------------------------------------------------------------
  415.  */
  416.  
  417. static void
  418. SquareDisplay(clientData)
  419.     ClientData clientData;    /* Information about window. */
  420. {
  421.     Square *squarePtr = (Square *) clientData;
  422.     Tk_Window tkwin = squarePtr->tkwin;
  423.     Pixmap pm = None;
  424.     Drawable d;
  425.  
  426.     squarePtr->updatePending = 0;
  427.     if (!Tk_IsMapped(tkwin)) {
  428.     return;
  429.     }
  430.  
  431.     /*
  432.      * Create a pixmap for double-buffering, if necessary.
  433.      */
  434.  
  435.     if (squarePtr->doubleBuffer) {
  436.     pm = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  437.         Tk_Width(tkwin), Tk_Height(tkwin),
  438.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  439.     d = pm;
  440.     } else {
  441.     d = Tk_WindowId(tkwin);
  442.     }
  443.  
  444.     /*
  445.      * Redraw the widget's background and border.
  446.      */
  447.  
  448.     Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->bgBorder,
  449.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  450.         squarePtr->borderWidth, squarePtr->relief);
  451.  
  452.     /*
  453.      * Display the square.
  454.      */
  455.  
  456.     Tk_Fill3DRectangle(Tk_Display(tkwin), d, squarePtr->fgBorder,
  457.         squarePtr->x, squarePtr->y, squarePtr->size, squarePtr->size,
  458.         squarePtr->borderWidth, TK_RELIEF_RAISED);
  459.  
  460.     /*
  461.      * If double-buffered, copy to the screen and release the pixmap.
  462.      */
  463.  
  464.     if (squarePtr->doubleBuffer) {
  465.     XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), squarePtr->gc,
  466.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  467.     XFreePixmap(Tk_Display(tkwin), pm);
  468.     }
  469. }
  470.  
  471. /*
  472.  *----------------------------------------------------------------------
  473.  *
  474.  * SquareDestroy --
  475.  *
  476.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  477.  *    to clean up the internal structure of a square at a safe time
  478.  *    (when no-one is using it anymore).
  479.  *
  480.  * Results:
  481.  *    None.
  482.  *
  483.  * Side effects:
  484.  *    Everything associated with the square is freed up.
  485.  *
  486.  *----------------------------------------------------------------------
  487.  */
  488.  
  489. static void
  490. SquareDestroy(clientData)
  491.     ClientData clientData;    /* Info about square widget. */
  492. {
  493.     Square *squarePtr = (Square *) clientData;
  494.  
  495.     Tk_FreeOptions(configSpecs, (char *) squarePtr, squarePtr->display, 0);
  496.     if (squarePtr->gc != None) {
  497.     Tk_FreeGC(squarePtr->display, squarePtr->gc);
  498.     }
  499.     ckfree((char *) squarePtr);
  500. }
  501.  
  502. /*
  503.  *----------------------------------------------------------------------
  504.  *
  505.  * KeepInWindow --
  506.  *
  507.  *    Adjust the position of the square if necessary to keep it in
  508.  *    the widget's window.
  509.  *
  510.  * Results:
  511.  *    None.
  512.  *
  513.  * Side effects:
  514.  *    The x and y position of the square are adjusted if necessary
  515.  *    to keep the square in the window.
  516.  *
  517.  *----------------------------------------------------------------------
  518.  */
  519.  
  520. static void
  521. KeepInWindow(squarePtr)
  522.     register Square *squarePtr;        /* Pointer to widget record. */
  523. {
  524.     int i, bd;
  525.     bd = 0;
  526.     if (squarePtr->relief != TK_RELIEF_FLAT) {
  527.     bd = squarePtr->borderWidth;
  528.     }
  529.     i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size);
  530.     if (i < 0) {
  531.     squarePtr->x += i;
  532.     }
  533.     i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size);
  534.     if (i < 0) {
  535.     squarePtr->y += i;
  536.     }
  537.     if (squarePtr->x < bd) {
  538.     squarePtr->x = bd;
  539.     }
  540.     if (squarePtr->y < bd) {
  541.     squarePtr->y = bd;
  542.     }
  543. }
  544.